<template>
    <n-grid x-gap="20" y-gap="20" responsive="screen" cols="1 l:2">
        <n-gi>
            <n-card title="菜单管理">
                <n-space :size="12" vertical>
                    <n-flex justify="space-between">
                        <n-space>
                            <n-button
                                :size="buttonSize"
                                @click="toggleExpand"
                                type="info"
                                secondary
                            >
                                <template #icon>
                                    <n-icon>
                                        <VerticalAlignCenterRound
                                            v-if="expandedKeys.length"
                                        />
                                        <ExpandRound v-else />
                                    </n-icon>
                                </template>
                                {{
                                    expandedKeys.length
                                        ? '收起全部'
                                        : '展开全部'
                                }}
                            </n-button>
                        </n-space>

                        <n-space>
                            <n-button
                                :size="buttonSize"
                                type="primary"
                                @click="onAction('top')"
                            >
                                <template #icon>
                                    <n-icon>
                                        <AddRound />
                                    </n-icon>
                                </template>
                                顶级菜单
                            </n-button>
                        </n-space>
                    </n-flex>

                    <n-input
                        v-model:value="pattern"
                        placeholder="菜单名称"
                        clearable
                    />

                    <n-tree
                        :data="<TreeOption[]>menuTree"
                        block-line
                        expand-on-click
                        checkable
                        v-model:expanded-keys="expandedKeys"
                        v-model:checked-keys="checkedKeys"
                        key-field="id"
                        show-line
                        :pattern="pattern"
                        :show-irrelevant-nodes="false"
                        virtual-scroll
                        style="
                            max-height: calc(
                                100vh - var(--header-height) - var(--gap) * 2 -
                                    177px
                            );
                        "
                        label-field="nodeTitle"
                        :render-label="renderLabel"
                        :selectable="true"
                        v-model:selected-keys="selectedKeys"
                        :loading="loading"
                    />
                </n-space>
            </n-card>
        </n-gi>

        <n-gi>
            <n-card title="编辑">
                <Form
                    v-if="showForm"
                    v-model:value="formState"
                    :items="formItems"
                    :loading="submiting"
                    label-width="6rem"
                    @submit="onSubmit"
                />
                <n-flex
                    v-else
                    style="height: 300px"
                    align="center"
                    justify="center"
                >
                    <n-result
                        :status="result.status"
                        :title="result.title"
                        :description="result.description"
                    />
                </n-flex>
            </n-card>
        </n-gi>
    </n-grid>

    <n-dropdown
        placement="bottom-start"
        trigger="manual"
        :x="xRef"
        :y="yRef"
        :options="dropdownOptions"
        :show="showDropdownRef"
        @select="handleSelect"
        :on-clickoutside="() => (showDropdownRef = false)"
    />
</template>

<script setup lang="ts">
import { authMenuAdd, authMenuDelete, authMenuUpdate } from '@/apis/auth/menu'
import { Form, FormItemsConfig } from '@/components/form'
import useState from '@/hooks/useState'
import { useAuthMenuStore } from '@/store'
import { IAuthMenuEditParameter } from '@/types/auth/menu'
import {
    AddRound,
    ExpandRound,
    VerticalAlignCenterRound,
    PlaylistAddRound,
    EditOffOutlined,
    DeleteForeverOutlined
} from '@vicons/material'
import {
    ButtonProps,
    DropdownOption,
    NButton,
    NFlex,
    NIcon,
    NSpace,
    ResultProps,
    TreeOption,
    TreeSelectOption,
    useDialog,
    useMessage
} from 'naive-ui'
import { storeToRefs } from 'pinia'
import { Component, computed, h, nextTick, reactive, ref } from 'vue'
import { hiddenOptions, ignoreCacheOptions } from './options'

const message = useMessage()
const menuStore = useAuthMenuStore()
const { menuTree, canExpandKeys, source, inited } = storeToRefs(menuStore)
const buttonSize: ButtonProps['size'] = 'small'

const result = reactive<ResultProps>({
    title: '休息一下',
    status: '418',
    description: '此时没有正在编辑的菜单'
})

// 加载列表
const { state: loading, setState: setLoading } = useState()
function getMenuList() {
    setLoading(true)
    inited.value = false
    menuStore
        .init()
        .catch((e: Error) => message.error(e.message))
        .finally(() => setLoading(false))
}

// 右键菜单
const selectedKeys = ref<number[]>([])
function renderIcon(icon: Component) {
    return () => {
        return h(NIcon, null, {
            default: () => h(icon)
        })
    }
}
type ActionType = 'top' | 'sub' | 'self' | 'delete'
const showDropdownRef = ref(false)
const xRef = ref(0)
const yRef = ref(0)
const dropdownOptions: DropdownOption[] = [
    {
        label: '编辑',
        key: 'self',
        icon: renderIcon(EditOffOutlined)
    },
    {
        label: '添加子菜单',
        key: 'sub',
        icon: renderIcon(PlaylistAddRound)
    },
    {
        label: '删除',
        key: 'delete',
        icon: renderIcon(DeleteForeverOutlined)
    }
]
const targetId = ref<number>()
const targetAction = ref<ActionType>()
function renderLabel({ option }: { option: TreeOption }) {
    const id = option.id as unknown as number
    return h(
        'div',
        {
            onContextmenu: ($event) => {
                selectedKeys.value = []
                selectedKeys.value.push(id)
                handleContextMenu($event, id)
            }
        },
        {
            default: () => option.nodeTitle
        }
    )
}

function handleSelect(key: ActionType) {
    showDropdownRef.value = false
    targetAction.value = key
    nextTick(() => onAction())
}
function handleContextMenu(e: MouseEvent, id: number) {
    e.preventDefault()
    showDropdownRef.value = false
    nextTick().then(() => {
        showDropdownRef.value = true
        xRef.value = e.clientX
        yRef.value = e.clientY
        targetId.value = id
    })
}

// 展开多选
const expandedKeys = ref<number[]>([...canExpandKeys.value])
const checkedKeys = ref<number[]>([])
function toggleExpand() {
    if (expandedKeys.value.length) {
        expandedKeys.value.length = 0
    } else {
        expandedKeys.value = [...canExpandKeys.value]
    }
}

// 过滤
const pattern = ref('')

// 编辑
const showForm = ref(false)
const defaultState: IAuthMenuEditParameter = {
    pid: null as any,
    title: '',
    path: '',
    component: '',
    hidden: 0,
    active: '',
    ignore_cache: 0
}
const formState = ref<IAuthMenuEditParameter>({ ...defaultState })
const treeSelectOptions = computed<TreeSelectOption[]>(() => {
    const map: { [key: number]: TreeSelectOption } = {}
    source.value.forEach((item) => {
        map[item.id] = {
            pid: item.id,
            key: item.id,
            label: item.title,
            children: [],
            disabled: targetId.value === item.id
        }
    })

    let arr: TreeSelectOption[] = []
    source.value.forEach((item) => {
        const node = map[item.id]

        if (item.pid) {
            const pNode = map[item.pid]
            if (pNode.disabled) {
                node.disabled = true
            }

            pNode.children?.push(node)
        } else {
            arr.push(node)
        }
    })

    return arr
})
const formItems = computed<FormItemsConfig<IAuthMenuEditParameter>>(() => {
    return [
        {
            field: 'pid',
            label: '父级菜单',
            component: 'TreeSelect',
            props: {
                options: treeSelectOptions.value,
                placeholder: '顶级菜单',
                defaultExpandAll: true,
                showPath: true,
                disabled: targetAction.value !== 'self'
            }
        },
        {
            field: 'title',
            label: '名称',
            component: 'Input',
            props: {
                placeholder: '菜单名称'
            },
            rule: {
                required: true,
                min: 2,
                max: 20,
                trigger: ['input', 'blur']
            }
        },
        {
            field: 'path',
            label: '访问路径',
            component: 'Input',
            props: {
                placeholder: '只需要填写当前文件夹路径'
            },
            rule: { required: true }
        },
        {
            field: 'component',
            label: '组件路径',
            component: 'Input',
            props: {
                placeholder: ''
            },
            feedback: '`/src/views`部分省略'
        },
        {
            field: 'icon',
            label: '图标',
            component: 'Input',
            props: {
                placeholder: '请选择图标'
            }
        },
        {
            field: 'sort',
            label: '排序',
            component: 'InputNumber',
            props: {
                min: 1,
                max: 9999
            },
            feedback: '正序排序'
        },
        {
            field: 'active',
            label: '高亮菜单',
            component: 'Input',
            props: {
                placeholder: '高亮的菜单'
            },
            feedback: '这里需要填写路由的name值'
        },
        {
            field: 'hidden',
            label: '在菜单隐藏',
            component: 'RadioGroup',
            props: {
                options: hiddenOptions,
                'default-value': 0
            }
        },
        {
            field: 'ignore_cache',
            label: '禁用缓存',
            component: 'RadioGroup',
            props: {
                options: ignoreCacheOptions,
                'default-value': 0
            }
        }
    ] as FormItemsConfig<IAuthMenuEditParameter>
})
function onAction(action?: ActionType) {
    if (!action) {
        action = targetAction.value
    } else {
        targetAction.value = action
        targetId.value = undefined
    }

    // 顶级菜单
    if (targetAction.value === 'top') {
        formState.value = { ...defaultState }
    }

    // 子菜单
    if (targetAction.value === 'sub') {
        formState.value = { ...defaultState, pid: targetId.value }
    }

    // 编辑
    if (targetAction.value === 'self') {
        // 找到对应菜单项
        const target = menuStore.getSourceItem(targetId.value!)
        formState.value = { ...(<IAuthMenuEditParameter>target) }
    }

    // 删除
    if (targetAction.value === 'delete') {
        handleDeleteConfirm()
    } else {
        showForm.value = true
    }
}

const dialog = useDialog()
function handleDeleteConfirm() {
    dialog.warning({
        title: '警告',
        content: '你确定删除这个菜单吗？',
        positiveText: '确定',
        negativeText: '取消',
        onPositiveClick: () => {
            handleDelete()
        }
    })
}

function handleDelete() {
    const msg = message.create('删除中...', {
        type: 'loading',
        duration: 100000
    })
    authMenuDelete({
        id: targetId.value!
    })
        .then(() => {
            msg.type = 'success'
            msg.content = '删除成功'
            msg.duration = 1000
            getMenuList()
        })
        .catch((e: Error) => {
            msg.type = 'error'
            msg.content = e.message
            msg.duration = 1000
        })
        .finally(() => (targetId.value = undefined))
}

const { state: submiting, setState: setSubmiting } = useState()
function onSubmit() {
    setSubmiting(true)
    const API = formState.value.id ? authMenuUpdate : authMenuAdd
    API(formState.value)
        .then(() => {
            result.status = 'success'
            result.title = '保存成功'
            result.description = '修改的信息已经同步到服务器'

            showForm.value = false
            getMenuList()
            message.success('操作成功')
        })
        .catch((e: Error) => {
            message.error(e.message)
        })
        .finally(() => setSubmiting(false))
}
</script>
